package com.lc.learn.netty.server;

import cn.hutool.core.exceptions.ExceptionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 服务端接收处理器
 * @author: LChuang
 * @date: 2023/10/28 13:51
 */
@Slf4j
public class NettyServerHandle extends ChannelInboundHandlerAdapter {

    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();


    /**
     * 客户端连接服务器
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress inetSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        // 获取连接通道唯一标识
        ChannelId channelId = ctx.channel().id();
        if (CHANNEL_MAP.containsKey(channelId)) {
            log.info("客户端【{}】是连接状态，连接通道数量: {}", channelId, CHANNEL_MAP.size());
        } else {
            // 保存连接
            CHANNEL_MAP.put(channelId, ctx);
            log.info("客户端【{}】连接netty服务器[IP:{}--->PORT:{}]", channelId, inetSocket.getAddress().getHostAddress(), inetSocket.getPort());
            log.info("连接通道数量: {}", CHANNEL_MAP.size());
        }
    }


    /**
     * 客户端断开连接
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress inetSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        ChannelId channelId = ctx.channel().id();
        if (CHANNEL_MAP.containsKey(channelId)) {
            CHANNEL_MAP.remove(channelId);
            log.info("客户端【{}】断开netty服务器[IP:{}-->PORT:{}]", channelId, inetSocket.getAddress().getHostAddress(), inetSocket.getPort());
            log.info("连接通道数量: {}", CHANNEL_MAP.size());
        }
    }


    /**
     * 客户端连接发生异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("客户端【{}】连接异常！此时通道连接数量：{}",ctx.channel().remoteAddress(), CHANNEL_MAP.size());
        log.error(ExceptionUtil.stacktraceToString(cause));
        ctx.close();
    }

    /**
     * 接收客户端发出的消息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("【{}】客户端发送消息：{}", ctx.channel().id(), msg);
        /*
         * 下面可以解析数据，保存数据，生成返回报文，将需要返回报文写入write函数
         */
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", "nettyServer get your msg");
        // 响应客户端
        this.channelWrite(ctx.channel().id(), map);
    }


    /**
     * 服务端对客户端做出响应
     *
     * @param channelId 连接通道的唯一id
     * @param data      响应数据
     */
    public void channelWrite(ChannelId channelId, Object data) throws Exception {
        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);
        if (Objects.isNull(ctx)) {
            log.info("客户端管道【{}】已断开连接！", channelId);
            return;
        }
        if (Objects.isNull(data)) {
            log.info("服务端向客户端【{}】响应数据为空！", channelId);
            return;
        }
        // 服务端端信息写入ctx
        ctx.write(data);
        // 刷新缓存区
        ctx.flush();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        String socketString = ctx.channel().remoteAddress().toString();
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("Client:{} READER_IDLE读超时！", socketString);
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("Client:{} WRITER_IDLE 写超时！", socketString);
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("Client:{} ALL_IDLE 总超时！", socketString);
                ctx.disconnect();
            }
        }
    }
}
